﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Cache;
using System.Text;
using Microsoft.Win32;

namespace HttpPublish
{
    public static class MultipartUpload
    {
        /// <summary>
        /// Multipart form post
        /// </summary>
        /// <param name="postUrl">The post URL.</param>
        /// <param name="cookies">The cookies or null.</param>
        /// <param name="postParameters">The post parameters. {name: post_nvp_name, value: file://full_file_path || string content to send}</param>
        /// <param name="config">Publish configuration</param>
        /// <returns></returns>
        public static HttpWebResponse Post(string postUrl, IEnumerable<Cookie> cookies, Dictionary<string, string> postParameters, PublishConfigration config)
        {
            ServicePointManager.Expect100Continue = false;
            
            var boundary = NewDataBoundary();

            var request = WebRequest.Create(postUrl) as HttpWebRequest;

            // Set up the request properties
            Debug.Assert(request != null, "request != null");
            request.Method = "POST";
            request.ContentType = "multipart/form-data; boundary=" + boundary;
            request.UserAgent = "PhasDocAgent 1.0";

            request.Timeout = 60000;
            request.AllowAutoRedirect = false;
            request.Credentials = new NetworkCredential(config.User, config.Password);
            request.PreAuthenticate = true;
            request.CachePolicy = new HttpRequestCachePolicy(HttpRequestCacheLevel.NoCacheNoStore);


            if (cookies != null)
            {
                request.CookieContainer = new CookieContainer();
                foreach (var cookie in cookies)
                {
                    request.CookieContainer.Add(cookie);
                }
            }

            #region WRITING STREAM
            using (var formDataStream = request.GetRequestStream())
            {
                foreach (var param in postParameters)
                {
                    if (param.Value.StartsWith("file://"))
                    {
                        var filepath = param.Value.Substring(7);

                        // Add just the first part of this param, since we will write the file data directly to the Stream
                        var header = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n",
                            boundary,
                            param.Key,
                            Path.GetFileName(filepath) ?? param.Key,
                            GetMimeType(filepath)
                        );

                        formDataStream.Write(Encoding.UTF8.GetBytes(header), 0, header.Length);

                        // Write the file data directly to the Stream, rather than serializing it to a string.

                        var buffer = new byte[2048];

                        var fs = new FileStream(filepath, FileMode.Open);

                        for (var i = 0; i < fs.Length; )
                        {
                            var k = fs.Read(buffer, 0, buffer.Length);
                            if (k > 0)
                            {
                                formDataStream.Write(buffer, 0, k);
                            }
                            i = i + k;
                        }
                        fs.Close();
                    }
                    else
                    {
                        var postData = string.Format("--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n",
                            boundary,
                            param.Key,
                            param.Value);
                        formDataStream.Write(Encoding.UTF8.GetBytes(postData), 0, postData.Length);
                    }
                }

                // Add the end of the request
                var footer = Encoding.UTF8.GetBytes("\r\n--" + boundary + "--\r\n");
                formDataStream.Write(footer, 0, footer.Length);
                request.ContentLength = formDataStream.Length;
                formDataStream.Close();
            }
            #endregion

            return request.GetResponse() as HttpWebResponse;
        }

        private static string NewDataBoundary()
        {
            var rnd = new Random();
            var formDataBoundary = "";
            while (formDataBoundary.Length < 15)
            {
                formDataBoundary = formDataBoundary + rnd.Next();
            }
            formDataBoundary = formDataBoundary.Substring(0, 15);
            formDataBoundary = "-----------------------------" + formDataBoundary;
            return formDataBoundary;
        }

        private static string GetMimeType(string fileName)
        {
            var mimeType = "application/unknown";

            var regKey = Registry.ClassesRoot.OpenSubKey(
                (Path.GetExtension(fileName) ?? ".txt").Remove(0, 1).ToLower()
                );

            if (regKey != null)
            {
                var contentType = regKey.GetValue("Content Type");

                if (contentType != null)
                    mimeType = contentType.ToString();
            }

            return mimeType;
        }
    }
}